""" scikit-learn metadata script """

import json
import os
import pydoc
import re
import sys


def _split_docstring(value):
    headers = {}
    current_header = ""
    current_lines = []
    lines = value.split("\n")
    index = 0
    while index < len(lines):
        if index + 1 < len(lines) and len(lines[index + 1].strip(" ")) > 0 and \
            len(lines[index + 1].strip(" ").strip("-")) == 0:
            headers[current_header] = current_lines
            current_header = lines[index].strip(" ")
            current_lines = []
            index = index + 1
        else:
            current_lines.append(lines[index])
        index = index + 1
    headers[current_header] = current_lines
    return headers

def _update_description(schema, lines):
    if len("".join(lines).strip(" ")) > 0:
        for i, value in enumerate(lines):
            lines[i] = value.lstrip(" ")
        schema["description"] = "\n".join(lines)

def _attribute_value(attribute_type, attribute_value):
    if attribute_value in ("None", "np.finfo(float).eps"):
        return None
    if attribute_type in ("float32", "int32", "boolean", "string"):
        if attribute_value in ("'auto'", '"auto"') or attribute_type == "string":
            return attribute_value.strip("'").strip('"')
    if attribute_type == "float32":
        return float(attribute_value)
    if attribute_type == "int32":
        return int(attribute_value)
    if attribute_type == "boolean":
        if attribute_value in ("True", "False"):
            return attribute_value == "True"
        raise ValueError(f"Unknown boolean default value '{str(attribute_value)}'.")
    if attribute_type:
        raise ValueError("Unknown default type '" + attribute_type + "'.")
    return attribute_value.strip("'")

def _find_attribute(schema, name):
    schema.setdefault("attributes", [])
    attribute = next((_ for _ in schema["attributes"] if _["name"] == name), None)
    if not attribute:
        attribute = { "name": name }
        schema["attributes"].append(attribute)
    return attribute

def _update_attributes(schema, lines):
    doc_indent = "    " if sys.version_info[:2] >= (3, 13) else "        "
    while len(lines) > 0:
        line = lines.pop(0)
        match = re.match(r"\s*(\w*)\s*:\s*(.*)\s*", line)
        if not match:
            raise SyntaxError("Expected ':' in parameter.")
        name = match.group(1)
        line = match.group(2)
        attribute = _find_attribute(schema, name)
        match = re.match(r"(.*),\s*default=(.*)\s*", line)
        default_value = None
        if match:
            line = match.group(1)
            default_value = match.group(2)
        attribute_types = {
            "float": "float32",
            "boolean": "boolean",
            "bool": "boolean",
            "str": "string",
            "string": "string",
            "int": "int32",
            "integer": "int32"
        }
        attribute_type = attribute_types.get(line, None)
        if default_value:
            attribute["default"] = _attribute_value(attribute_type, default_value)
        description = []
        while len(lines) > 0:
            if lines[0].strip() != "" and not lines[0].startswith(doc_indent):
                break
            line = lines.pop(0).lstrip(" ")
            description.append(line)
        attribute["description"] = "\n".join(description)

def _metadata():
    root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
    json_file = os.path.join(root_dir, "source", "sklearn-metadata.json")
    with open(json_file, encoding="utf-8") as file:
        json_root = json.loads(file.read())

    for schema in json_root:
        name = schema["name"]
        skip_modules = [
            "lightgbm.",
            "sklearn.svm.classes",
            "sklearn.ensemble.forest.",
            "sklearn.ensemble.weight_boosting.",
            "sklearn.neural_network.multilayer_perceptron.",
            "sklearn.tree.tree."
        ]
        if not any(name.startswith(module) for module in skip_modules):
            class_definition = pydoc.locate(name)
            if not class_definition:
                raise KeyError("'" + name + "' not found.")
            docstring = class_definition.__doc__
            if not docstring:
                raise Exception("'" + name + "' missing __doc__.")
            headers = _split_docstring(docstring)
            if "" in headers:
                _update_description(schema, headers[""])
            if "Parameters" in headers:
                _update_attributes(schema, headers["Parameters"])

    with open(json_file, "w", encoding="utf-8") as file:
        file.write(json.dumps(json_root, sort_keys=False, indent=2))

def main():
    _metadata()

if __name__ == "__main__":
    main()
